package jmine.tec.batch.domain;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Constraint;
import jmine.tec.persist.annotation.DiscriminatorComment;

import org.hibernate.annotations.Type;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.bussobj.PersistableBusinessObject;
import bancosys.tec.utils.date.Timestamp;

/**
 * Represanta a execução de um {@link MProcess}. O processo será executado sobre unidades de processamento. <br>
 * Essa classe guarda o estado da execução do processo.
 * 
 * @author Frank Cara
 * @see ProcessingUnit
 * @see MProcess
 */
@Entity
@Table(name = "PROCESS")
@Alias("PROC")
@SequenceGenerator(name = "SEQ_PROC", sequenceName = "SEQ_PROC")
@DiscriminatorValue(value = "GENERICO")
@DiscriminatorComment("PROCESSAMENTO GENERICO")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
@Comment(table = "PROCESS", column = "DISCRIMINATOR", value = "IDENTIFICADOR DO TIPO DO PROCESSO.")
@Documentation("ARMAZENA OS PROCESSAMENTOS EXECUTADOS DAS CARTEIRAS COMO VIRADA DE DATA DA CONTROLADORIA, VIRADA DE DATA DA CUSTODIA, REPROCESSAMENTO, ETC.")
@Constraint(suffix = "1")
public class Process extends PersistableBusinessObject {
    /**
     * Indica que o processamento ainda não começou.
     */
    public static final int NOT_STARETD = 0;

    /**
     * Indica que o processamento já foi iniciado.
     */
    public static final int STARETD = 1;

    /**
     * Indica que o processamento foi finalizado sem erros.
     */
    public static final int FINISHED = 2;

    /**
     * Indica que o processamento foi finalizado com erros.
     */
    public static final int ERROR = 3;

    private Long id;

    private Timestamp startTime;

    private Timestamp finishTime;

    private int status = NOT_STARETD;

    private int qtdUnits = 0;

    private int qtdSucessUnits = 0;

    private int qtdUnsucessUnits = 0;

    /** Version para lock otimista */
    // private Integer version;
    /**
     * @return o valor do id.
     */
    @Id
    @Column(name = "COD_PROCESS")
    @Documentation("CODIGO DO PROCESSO EXECUTADO.")
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_PROC")
    public Long getId() {
        return this.id;
    }

    /**
     * Seta o valor do id.
     * 
     * @param id o valor do id.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @return o valor da hora de inicio do processo.
     */
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Column(name = "TS_START")
    @Documentation("HORARIO INICIAL DE EXECUCAO DO PROCESSO.")
    public Timestamp getStartTime() {
        return this.startTime;
    }

    /**
     * Seta o valor da hora de inicio do processo.
     * 
     * @param startTime a hora de inicio do processo.
     */
    public void setStartTime(Timestamp startTime) {
        if (this.startTime == null) {
            this.startTime = startTime;
        }
    }

    /**
     * @return a hora final do processo.
     */
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Column(name = "TS_FINISH")
    @Documentation("HORARIO FINAL DE EXECUCAO DO PROCESSO.")
    public Timestamp getFinishTime() {
        return this.finishTime;
    }

    /**
     * Seta a hora final do processo.
     * 
     * @param finishTime a hora final do processo.
     */
    public void setFinishTime(Timestamp finishTime) {
        if (this.finishTime == null || this.finishTime.before(finishTime)) {
            this.finishTime = finishTime;
        }
    }

    /**
     * @return o status do processo.
     */
    @Column(name = "STATUS", nullable = false)
    @Documentation("INDICA O ESTADO DO PROCESSO, QUE PODE SER: NAO INICIADO (0), INICIADO (1), FINALIZADO SEM ERROS (2), FINALIZADO COM ERROS (3).")
    @Constraint(value = "CK_PROC_0", condition = "STATUS IN (0, 1, 2, 3)")
    public int getStatus() {
        return this.status;
    }

    /**
     * Seta o status do processo.
     * 
     * @param status o status do processo.
     */
    public void setStatus(int status) {
        this.status = status;
    }

    /**
     * @return a quantidade de ProcessingUnits do Process.
     */
    @Column(name = "QTY_UNITS", nullable = false)
    @Documentation("QUANTIDADE DE UNIDADES DE PROCESSAMENTO.")
    public int getQtdUnits() {
        return this.qtdUnits;
    }

    /**
     * Seta a quantidade de unidades de processamento do processo.
     * 
     * @param qtdUnits a quantidade de unidades de processamento do processo.
     */
    public void setQtdUnits(int qtdUnits) {
        this.qtdUnits = qtdUnits;
    }

    /**
     * @return a quantidade de ProcessingUnits finalizadas com sucess do Process.
     */
    @Column(name = "QTY_SUC_UNITS", nullable = false)
    @Documentation("QUANTIDADE DE UNIDADES DE PROCESSAMENTO FINALIZADAS COM SUCESSO.")
    public int getQtdSucessUnits() {
        return this.qtdSucessUnits;
    }

    /**
     * Seta a quantidade de unidades de processamento bem sucedidas do processo.
     * 
     * @param qtdSucessUnits a quantidade de unidades de processamento bem sucedidas do processo.
     */
    public void setQtdSucessUnits(int qtdSucessUnits) {
        this.qtdSucessUnits = qtdSucessUnits;
    }

    /**
     * @return a quantidade de ProcessingUnits não finalizadas com sucesso do Process.
     */
    @Column(name = "QTY_UNSUC_UNITS", nullable = false)
    @Documentation("QUANTIDADE DE UNIDADES DE PROCESSAMENTO MAL SUCEDIDAS.")
    public int getQtdUnsucessUnits() {
        return this.qtdUnsucessUnits;
    }

    /**
     * Seta a quantidade de unidades de processamento mal sucedidas do processo.
     * 
     * @param qtdUnsucessUnits a quantidade de unidades de processamento mal sucedidas do processo.
     */
    public void setQtdUnsucessUnits(int qtdUnsucessUnits) {
        this.qtdUnsucessUnits = qtdUnsucessUnits;
    }

    /**
     * Updates the counters of this {@link Process} in order to account for successful and unsuccessful {@link ProcessingUnit}.
     * 
     * @param unit the {@link ProcessingUnit}
     */
    public void notifyProcessingUnitFinished(ProcessingUnit<?> unit) {
        if (this.equals(unit.getProcess())) {
            switch (unit.getStatus()) {
            case ProcessingUnit.FINISHED:
                this.qtdSucessUnits++;
                break;
            case ProcessingUnit.ERROR:
                this.qtdUnsucessUnits++;
                break;
            default:
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "[" + this.getId() + "]";
    }

}
